﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using SLNGP.Core.Fitness;
using SLNGP.Core.Genes;
using SLNGP.Core.GeneticOpearators;
using SLNGP.Core.Initialization;
using SLNGP.Core.Run;
using SLNGP.Core.SecondaryStrategies;
using SLNGP.Core.StopRun;

namespace SLNGP.Core.GeneticPrograms
{
    public interface IGeneticProgram
    {
        /// <summary>
        /// List of all possible genes that could be used in the GP run. 
        /// All functions, procedures and terminals that could occur in the GP.
        /// </summary>
        List<IGeneDefinition> PossibleGenes { get; }

        /// <summary>
        /// The fitness function used to determine the fitness of an individual. 
        /// </summary>
        IFitnessFunction FitnessFunction { get; set; }

        /// <summary>
        /// Conditions for stopping genetic programming runs. 
        /// </summary>
        IStopRunConditions StopRunConditions { get; set; }

        /// <summary>
        /// The results of the last GP run.
        /// </summary>
        IGeneticProgrammingRunResults LastRunResults { get; set; }

        /// <summary>
        /// Starts the GP until the stop conditions are met.  
        /// </summary>
        void RunUntilStopCondition();

        /// <summary>
        /// Utility method used to perform this.PossibleGenes.Add() for each of the specified gene defitions. 
        /// </summary>
        /// <param name="geneDefinitions">A comma seperated list of gene definitions.</param>
        void AddPossibleGenes(params IGeneDefinition[] geneDefinitions);

        /// <summary>
        /// An ordered list of all genetic opearators to execute in each generation. 
        /// </summary>
        ObservableCollection<IGeneticOpearator> GeneticOpearators { get; set; }

        /// <summary>
        /// The strategy persued to initialize the first generation in the run. 
        /// </summary>
        IFirstGenerationInitialization FirstGenerationInitialization { get; set; }

        /// <summary>
        /// Gets a trivial strategy used by some opearator in the genetic programming run. 
        /// </summary>
        /// <typeparam name="T">The INTERFACE type of the Strategy to retrieve.</typeparam>
        /// <returns>Returns a Strategy that implements that interface. If not found returns null.</returns>
        T GetStrategy<T>()
            where T: IGPStrategy;

        /// <summary>
        /// Sets a trivial strategy used by some opearator in the genetic programming run. 
        /// If the strategy implementing that interface already exists, it is overwritten. 
        /// </summary>
        /// <typeparam name="T">The INTERFACE type of the Strategy to set.</typeparam>
        void SetStrategy<T>(T t)
            where T : IGPStrategy;

        /// <summary>
        /// Sets a genetic opearator. 
        /// If T wasn't an existing genetic opearator it is added as the last genetic operator.
        /// If T was an existing genetic opearator, it is replaced and reserves it's execution slot. 
        /// </summary>
        /// <typeparam name="T">Type of Genetic Opearator to add.</typeparam>
        /// <param name="t">New Genetic Opearator.</param>
        void SetGeneticOpearator<T>(T t)
            where T : class, IGeneticOpearator;

        /// <summary>
        /// Gets a genetic opearator implementing a the generic parameter.
        /// </summary>
        /// <typeparam name="T">Type of class or interface implemented by the GeneticOpearator.</typeparam>
        /// <returns>null if no genetic opearator was found, or the genetic opearator.</returns>
        T GetGeneticOpearator<T>() 
            where T : class, IGeneticOpearator;
    }
}